﻿/*
 * Copyright © 2020, Wuxi Safover Intelligent Technology Co., Ltd. All rights reserved.
 * 
 * 无锡安永智能科技有限公司版权所有，未经许可，不得用于商业用途
 * 
 * Visit: https://www.safover.com/ 
 */

using log4net;
using System;
using System.Collections.Generic;
using System.Reflection;
using Safover.LidarCollector.Extensions;
using Safover.LidarCollector.Models;
using Safover.LidarCollector.Config;
using static Safover.LidarCollector.Managers.DeviceManager;
using Safover.Lidar.DataContract;
using System.Text.RegularExpressions;

namespace Safover.LidarCollector.Managers
{
    internal abstract class SingleScan
    {
        protected static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

        protected abstract string Name { get; }

        /// <summary>
        /// 周期性采集参数
        /// </summary>
        internal PeriodicScanParams PeriodicScanParams { get; }

        /// <summary>
        /// 业务工作模式
        /// </summary>
        protected abstract LidarWorkMode LidarWorkMode { get; }

        /// <summary>
        /// 雷达扫描模式
        /// </summary>
        protected abstract LidarScanMode LidarScanMode { get; }

        /// <summary>
        /// 单次采集超时阈值，单位=秒
        /// 
        /// 规则 = 【采集卡累加次数】除以【激光器频率 x 1000】 + 延时余量
        /// </summary>
        protected int SingleAcquisitionTimeout
        {
            get
            {
                var cFrequency = RuntimeConfigManager.Instance.AcquisitionCardConfig.CumulativePulses;
                var laserFrequency = RuntimeConfigManager.Instance.OpticalMaserConfig.Frequency;
                var offset = 15;

                var total = cFrequency / (laserFrequency * 1000) + offset;

                return (int)total;
            }
        }

        internal SingleScan(PeriodicScanParams periodicScanParams)
        {
            PeriodicScanParams = periodicScanParams;
        }

        protected void StartCatchingData(DataCacher cacher, TimeSpan timeSpan)
        {
            if (!DataCollector.ShouldContinueCollection)
            {
                return;
            }

            cacher.StartCache(timeSpan);
        }

        protected void FinishSingleScan(DataCacher cacher, float startAngle, float endAngle, float horizontalAngle, float vertialAngle)
        {
            if (!DataCollector.ShouldContinueCollection)
            {
                return;
            }

            var gpsInfo = DeviceFactoryInstance.GpsLocatorDevice.ObtainGpsInformation();

            var acAngles = DeviceFactoryInstance.ElectronicCompass.QueryAngles();

            var channels = new List<LidarRawChannel>();
            var acConfig = RuntimeConfigManager.Instance.AcquisitionCardConfig;
           
            foreach (var channel in cacher.GetCachedData())
            {
                var channelId = channel.Key;
                long[] data = ConvertToLong(channel.Value);

                if (data.Length != acConfig.SamplingDepth)
                {
                    throw new Exception($"channel Id=[{channelId}], expected data length=[{acConfig.SamplingDepth}], actual data length=[{data.Length}]");
                }
                
                channels.Add(new LidarRawChannel
                {
                    ChannelId = channelId,
                    Data = data,
                    SamplingDepth = acConfig.SamplingDepth,
                    SpatialResolution = (float)acConfig.SpatialResolution,
                    ChannelName = acConfig.GetChannelName(channelId),
                    CumulativePulses = acConfig.CumulativePulses
                });
            }

            var frame = new LidarRawFrame()
            {
                ShortUUID = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", ""),
                StationAbbreviation = RuntimeConfigManager.Instance.OthersConfig.StationAbbreviation,
                LidarWorkMode = LidarWorkMode,

                UtcPeriodicScanStartTimestamp = TimeUtils.BeiJingTimeToUtcTimestamp(PeriodicScanParams.UtcPeriodicScanStartTime),
                UtcStartTimestamp = TimeUtils.BeiJingTimeToUtcTimestamp(cacher.CacheStartTime),
                UtcEndTimestamp = TimeUtils.BeiJingTimeToUtcTimestamp(cacher.CacheEndTime),
                LidarScanMode = LidarScanMode,

                StartAngle = startAngle,
                EndAngle = endAngle,
                HorizontalAngle = horizontalAngle,
                VerticalAngle = vertialAngle,

                Longitude = gpsInfo.Longitude,
                Latitude = gpsInfo.Latitude,
                Altitude = gpsInfo.Altitude,

                PitchAngle = acAngles == null ? 0 : ((ElectronicCompassAngles)acAngles).PitchAngle,

                HeadingAngle = acAngles == null ? 0 : ((ElectronicCompassAngles)acAngles).HeadingAngle,

                Channels = channels.ToArray()
            };

            LidarRawFrameManager.Process(frame);
        }

        private long[] ConvertToLong(double[] value)
        {
            var list = new List<long>();

            foreach (var d in value)
            {
                list.Add(Convert.ToInt64(d));
            }

            return list.ToArray();
        }

        protected bool MoveRotaryTableHorizontally(float horizontalAngle)
        {
            try
            {
                return DeviceFactoryInstance.RotaryTableDevice.MoveHorizontallyTo(horizontalAngle);
            }
            catch (Exception e)
            {
                throw new RotaryTableControlException("Failed Moving Rotary Table Horizontally.", e);
            }
        }

        protected bool MoveRotaryTableVertically(float verticalAngle)
        {
            try
            {
                return DeviceFactoryInstance.RotaryTableDevice.MoveVerticallyTo(verticalAngle);
            }
            catch (Exception e)
            {
                throw new RotaryTableControlException("Failed Moving Rotary Table Vertically.", e);
            }
        }
    }
}
